/* 2021-02-05 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>  // shared memory
#include <sys/sem.h>  // semaphore
#include <sys/msg.h>  // message queue
#include <string.h>   // memcpy

// 消息队列结构
struct msg_from { 
    long mtype;
    char mtext;
};

// 联合体，用于semctl初始化
union semun {
    int              val; /* for SETVAL */
    struct semid_ds *buf;
    unsigned short  *array;
};

// 信号量初始化
int init_sem(int sem_id, int value) {
    union semun tmp;
    tmp.val = value;
    if (semctl(sem_id, 0, SETVAL, tmp) == -1) {
        perror("Init Semaphore Error");
        return -1;
    }
    return 0;
}

// P操作
//    若信号量值为1，获取资源并将信号量值-1
//    若信号量值为0，进程挂起等待
int sem_p(int sem_id) {
    struct sembuf sbuf;
    sbuf.sem_num = 0; // 序号
    sbuf.sem_op = -1; // P操作
    sbuf.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sbuf, 1) == -1) {
        perror("P operation Error");
        return -1;
    }
    return 0;
}

// V操作
//    释放资源将信号量值+1
//    如果有进程正在挂起等待，则唤醒它们
int sem_v(int sem_id) {
    struct sembuf sbuf;
    sbuf.sem_num = 0; // 序号
    sbuf.sem_op = 1; // V操作
    sbuf.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sbuf, 1) == -1) {
        perror("V operation Error");
        return -1;
    }
    return 0;
}

//删除信号量
int del_sem(int sem_id) {
    union semun tmp;
    if (semctl(sem_id, 0, IPC_RMID, tmp) == -1) {
        perror("Delete Semaphore Error");
        return -1;
    }
    return 0;
}

// 创建一个信号量集
int create_sem(key_t key) {
    int sem_id;
    if ((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1) {
        perror("semget error");
        exit(-1);
    }
    init_sem(sem_id, 1); /* 初始设为1资源未被占用 */
    return sem_id;
}

int main(int argc, char **argv) {
    key_t key;
    int shmid, semid, msqid;
    char *shm;
    char data[] = "this is server";
    struct shmid_ds buf1;  /* 用于删除共享内存 */
    struct msqid_ds buf2;  /* 用于删除消息队列 */
    struct msg_from msg;   /* 消息队列用于通知对方更新了共享内存 */

    // 获取key值
    if ((key = ftok(".", 'z')) < 0) {
        perror("ftok error");
        exit(1);
    }

    // 创建共享内存
    if ((shmid = shmget(key, 1024, IPC_CREAT|0666)) == -1) {
        perror("shmget error");
        exit(1);
    }

    // 连结共享内存
    shm = (char *)shmat(shmid, 0, 0);
    if ((int)shm == -1) {
        perror("Attach Shared Memory Error");
        exit(1);
    }

    // 创建消息队列
    if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) {
        perror("msgget error");
        exit(1);
    }

    // 创建信号量
    semid = create_sem(key);    

    while(1) {
        msgrcv(msqid, &msg, 1, 888, 0); // 读取类型为888的消息
        if (msg.mtext == 'q') {
            break;
        } else if (msg.mtext == 'r') {
            sem_p(semid);
            printf("%s\n", shm);
            sem_v(semid);
        }
    }

    // 断开连结
    shmdt(shm);

    /* 删除共享内存，消息队列，信号量 */
    shmctl(shmid, IPC_RMID, &buf1);
    msgctl(msqid, IPC_RMID, &buf2);
    del_sem(semid);

    return 0;
}


